import React, { useRef } from 'react';
import ReactEcharts, { EventMap } from 'echarts-for-react';
import { EChartOption } from 'echarts';
import axios from 'axios';
import { message } from 'antd';
import moment from 'moment';
import { useHistory } from 'react-router-dom';
import { useEffectOnce } from '../../utils/hooks';
import backIcon from '../../icons/back.svg';
import pauseIcon from '../../icons/pause.svg';
import startIcon from '../../icons/start.svg';
import leftIcon from '../../icons/left.svg';
import rightIcon from '../../icons/right.svg';

interface MonitorChartProps {
  connected: boolean; // 采集卡是否已经连接
  name: string; // 采集卡名称
  unit: string; // 采集卡采集到的数据的单位
  samplesPerSecond: number; // 采集卡每秒采集的样本点数目
  maxPointsInDynamicGraph: number; // 展示动态数据时，图中最多展示多少个点
  detailedPointsCount: number; // 展示某个点附近历史数据详情时，一次展示多少个数据点
  detailedPointsMoveSteps: number; // 展示某个点附近历史数据详情时，点击一次左移或者右移，一次移动多少个数据点
}

interface Sample {
  timeUnixNano: string;
  data: number;
}

interface DetailedSamples {
  timeUnixNano: string;
  samples: number[];
}

interface SampleInfo {
  timeUnixNanoNum: number;
  timeUnixNanoStr: string;
  // alreadyGot 该时间点已经获取到的数据点个数（用于之后左移、右移）
  alreadyGot: number;
}

const MonitorChart = (props: MonitorChartProps) => {
  // 类型用 any 而没用 ReactEcharts ，因为 ReactEcharts 的声明文件没给 getEchartsInstance...
  const echartRef = useRef<any>(null);
  // detailedSampleInfoRef 当前展示的历史详细数据，每个数据点的信息
  const detailedSampleInfoRef = useRef<SampleInfo[]>([]);
  // pauseStatus 是否处于暂停的状态（当“正在获取动态数据而非详细历史数据”的时候才有效（即showingDetail 为 false））
  let pauseStatus = false;
  // showingDetail true 处于展示详细数据的状态 false 正在获取动态数据的状态
  let showingDetail = false;
  let history = useHistory();
  const series = {
    name:
      props.name +
      (props.connected ? '（' + props.samplesPerSecond + ' samples / second）' : '（未连接）'),
    type: 'line',
    showSymbol: true,
    symbolSize: 3,
    hoverAnimation: true,
    data: [] as Sample[] | number[]
  };
  const leftButton = {
    // 左移 按钮
    type: 'image',
    id: 'left',
    right: 80,
    top: 20,
    z: -10,
    bounding: 'raw',
    origin: [75, 75],
    style: {
      image: leftIcon,
      width: 30,
      height: 30
    },
    onclick: () => {
      if (!showingDetail || detailedSampleInfoRef.current.length <= 0) {
        // 点击只在查看数据详情时有效
        return;
      }
      const endTimeUnixNano =
        detailedSampleInfoRef.current[detailedSampleInfoRef.current.length - 1].timeUnixNanoStr;
      const endTimeAlreadyGot =
        detailedSampleInfoRef.current[detailedSampleInfoRef.current.length - 1].alreadyGot;
      axios
        .get('/detailedDacDataAfter', {
          params: {
            name: props.name,
            timeUnixNano: endTimeUnixNano,
            alreadyGot: endTimeAlreadyGot,
            targetCount: props.detailedPointsMoveSteps
          }
        })
        .then((response) => {
          const newDetailedSampleInfos: SampleInfo[] = [];
          const newDetailedSamples: number[] = [];
          response.data.forEach((sample: DetailedSamples) => {
            sample.samples.forEach((s) => {
              newDetailedSamples.push(s);
              newDetailedSampleInfos.push({
                timeUnixNanoNum: Number(sample.timeUnixNano) / 1000000,
                timeUnixNanoStr: sample.timeUnixNano,
                alreadyGot: sample.samples.length
              });
            });
          });
          series.data.splice(0, newDetailedSamples.length);
          detailedSampleInfoRef.current.splice(0, newDetailedSampleInfos.length);
          series.data.splice(series.data.length, 0, ...newDetailedSamples);
          detailedSampleInfoRef.current.splice(series.data.length, 0, ...newDetailedSampleInfos);
          if (echartRef?.current) {
            option.dataZoom = undefined;
            option.xAxis = { type: 'category' };
            option.series = [series];
            echartRef.current.getEchartsInstance().setOption(option, true);
          }
        })
        .catch((error) => {
          if (error.response?.status === 401) {
            message.error('请先登录！');
            history.replace('/login');
          }
        });
    }
  };
  const rightButton = {
    // 右移 按钮
    type: 'image',
    id: 'right',
    right: 50,
    top: 20,
    z: -10,
    bounding: 'raw',
    origin: [75, 75],
    style: {
      image: rightIcon,
      width: 30,
      height: 30
    },
    onclick: () => {
      if (!showingDetail || detailedSampleInfoRef.current.length <= 0) {
        // 点击只在查看数据详情时有效
        return;
      }
      const startTimeUnixNano = detailedSampleInfoRef.current[0].timeUnixNanoStr;
      const startTimeAlreadyGot = detailedSampleInfoRef.current[0].alreadyGot;
      axios
        .get('/detailedDacDataBefore', {
          params: {
            name: props.name,
            timeUnixNano: startTimeUnixNano,
            alreadyGot: startTimeAlreadyGot,
            targetCount: props.detailedPointsMoveSteps
          }
        })
        .then((response) => {
          const newDetailedSampleInfos: SampleInfo[] = [];
          const newDetailedSamples: number[] = [];
          response.data.forEach((sample: DetailedSamples) => {
            sample.samples.forEach((s) => {
              newDetailedSamples.push(s);
              newDetailedSampleInfos.push({
                timeUnixNanoNum: Number(sample.timeUnixNano) / 1000000,
                timeUnixNanoStr: sample.timeUnixNano,
                alreadyGot: sample.samples.length
              });
            });
          });
          series.data.splice(-newDetailedSamples.length, newDetailedSamples.length);
          detailedSampleInfoRef.current.splice(
            -newDetailedSampleInfos.length,
            newDetailedSampleInfos.length
          );
          series.data.splice(0, 0, ...newDetailedSamples);
          detailedSampleInfoRef.current.splice(0, 0, ...newDetailedSampleInfos);
          if (echartRef?.current) {
            option.dataZoom = undefined;
            option.xAxis = { type: 'category' };
            option.series = [series];
            echartRef.current.getEchartsInstance().setOption(option, true);
          }
        })
        .catch((error) => {
          if (error.response?.status === 401) {
            message.error('请先登录！');
            history.replace('/login');
          }
        });
    }
  };
  const pauseOrStartButton = {
    // 暂停启动 按钮，点击后 查看动态数据中，切换暂停与启动状态
    type: 'image',
    id: 'pauseOrStart',
    right: 50,
    top: 20,
    z: -10,
    bounding: 'raw',
    origin: [75, 75],
    style: {
      image: pauseIcon,
      width: 30,
      height: 30
    },
    onclick: () => {}
  };
  const backButton = {
    // 返回 按钮，点击后，由 查看详细历史数据状态 返回 查看动态数据状态
    type: 'image',
    id: 'back',
    right: 20,
    top: 20,
    z: -10,
    bounding: 'raw',
    origin: [75, 75],
    style: {
      image: backIcon,
      width: 30,
      height: 30
    },
    onclick: function() {
      showingDetail = false;
      start();
      hideButtons('back', 'left', 'right');
      showButtons(pauseOrStartButton);
    }
  };
  let graphic: any[] = [pauseOrStartButton];
  const pause = () => {
    pauseOrStartButton.style.image = startIcon;
    pauseStatus = true;
    if (echartRef?.current) {
      echartRef.current.getEchartsInstance().setOption({ graphic });
    }
  };
  const start = () => {
    pauseOrStartButton.style.image = pauseIcon;
    pauseStatus = false;
    if (echartRef?.current) {
      echartRef.current.getEchartsInstance().setOption({ graphic });
    }
  };
  const switchPauseStart = () => {
    if (pauseStatus) {
      start();
    } else {
      pause();
    }
  };
  pauseOrStartButton.onclick = switchPauseStart;
  const dataZoom: EChartOption.DataZoom[] = [
    {
      type: 'slider',
      xAxisIndex: [0],
      filterMode: 'filter'
    }
  ];
  const option: EChartOption = {
    title: {
      text:
        props.name +
        (props.connected ? '（' + props.samplesPerSecond + ' samples / second）' : '（未连接）')
    },
    tooltip: {
      trigger: 'item',
      formatter: (params: any) => {
        const f = (time: number, data: number): string => {
          return '时间：' + moment(time).format('YYYY-MM-DD HH:mm:ss') + '<br/>数据值：' + data;
        };
        if (params.data.length > 1) {
          return f(params.data[0], params.data[1]);
        }
        return f(detailedSampleInfoRef.current[params.dataIndex].timeUnixNanoNum, params.data);
      },
      axisPointer: {
        animation: false,
        snap: true
      }
    },
    dataZoom,
    xAxis: {
      type: 'time',
      splitLine: {
        show: false
      }
    },
    yAxis: {
      type: 'value',
      name: '单位：' + props.unit,
      boundaryGap: [0, '100%'],
      splitLine: {
        show: false
      }
    },
    series: [series],
    graphic
  };
  const eventsDict: EventMap = {
    click: (params) => {
      if (showingDetail) {
        // 点击只在动态数据中有效，查看数据详情时无效
        return;
      }
      if (params.componentType === 'series' && params.seriesType === 'line') {
        // 点击图中某个点查看详情
        // 进入showingDetail状态
        showingDetail = true;
        hideButtons('pauseOrStart');
        showButtons(backButton, leftButton, rightButton);
        axios
          .get('/detailedDacData', {
            params: {
              name: props.name,
              timeUnix: Math.round(params.data[0] / 1000),
              targetCount: props.detailedPointsCount
            }
          })
          .then((response) => {
            const samples: number[] = [];
            const sampleInfos: SampleInfo[] = [];
            response.data.forEach((sample: DetailedSamples) => {
              sample.samples.forEach((s) => {
                samples.push(s);
                sampleInfos.push({
                  timeUnixNanoNum: Number(sample.timeUnixNano) / 1000000,
                  timeUnixNanoStr: sample.timeUnixNano,
                  alreadyGot: sample.samples.length
                });
              });
            });
            series.data = samples;
            detailedSampleInfoRef.current = sampleInfos;
            if (echartRef?.current) {
              // 用 ref 并直接找到 echarts instance 调用 setOption 而不是用 将 option 放入 state 中直接更新 option
              // 否则重新渲染整个 echarts ，动态数据显示不流畅
              // notMerge 为 true，不与原来的option合并，否则dataZoom组件无法消失
              option.dataZoom = undefined;
              option.xAxis = { type: 'category' };
              option.series = [series];
              echartRef.current.getEchartsInstance().setOption(option, true);
            }
          })
          .catch((error) => {
            if (error.response?.status === 401) {
              message.error('请先登录！');
              history.replace('/login');
            }
          });
      }
    }
  };

  const showButtons = (...buttons: any[]) => {
    buttons.forEach((button) => {
      for (let existButton of graphic) {
        if (existButton.id === button.id) {
          return;
        }
      }
      graphic.push(button);
    });
    if (echartRef?.current) {
      echartRef.current.getEchartsInstance().setOption({ graphic });
    }
  };

  const hideButtons = (...buttonIDs: string[]) => {
    graphic = graphic.filter((e) => {
      for (let buttonID of buttonIDs) {
        if (e.id === buttonID) {
          return false;
        }
      }
      return true;
    });
    if (echartRef?.current) {
      // notMerge 为 true，不与原来的option合并，否则graphic会残留有之前的按钮，无法隐藏按钮
      option.graphic = graphic;
      echartRef.current.getEchartsInstance().setOption(option, true);
    }
  };

  useEffectOnce(() => {
    const samples: Sample[] = [];
    // wss 连接的url是 https 时的 websocket 需要用 wss 否则用 ws
    const ws = new WebSocket(`ws://localhost:8080/dacDataPush?name=${props.name}`);
    ws.onmessage = function(event) {
      let newData = JSON.parse(event.data);
      samples.push(
        ...newData.map((sample: Sample) => [
          Number(sample.timeUnixNano) / 1000000,
          sample.data.toFixed(6)
        ])
      );
      if (samples.length > props.maxPointsInDynamicGraph) {
        samples.splice(0, samples.length - props.maxPointsInDynamicGraph);
      }
      if (!pauseStatus && !showingDetail && echartRef?.current) {
        // 用 ref 并直接找到 echarts instance 调用 setOption 而不是用 将 option 放入 state 中直接更新 option
        // 否则重新渲染整个 echarts ，动态数据显示不流畅
        series.data = samples;
        echartRef.current.getEchartsInstance().setOption({
          dataZoom,
          xAxis: {
            type: 'time',
            splitLine: {
              show: false
            }
          },
          series: [series]
        });
      }
    };
  });
  return <ReactEcharts ref={echartRef} option={option} onEvents={eventsDict} />;
};

export default MonitorChart;
